package org.litepal.crud;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.litepal.tablemanager.Connector;

import android.database.sqlite.SQLiteDatabase;

/**
 * 
 * @author Tony Green
 * @since 1.1
 */
public abstract class DataSupport {
	public static final String TAG = "DataSupport";

	/**
	 * The identify of each model. LitePal will generate the value
	 * automatically. Do not try to assign or modify it.
	 */
	private long baseObjId;

	private List<DataSupport> associatedModels = new ArrayList<DataSupport>();

	private Map<DataSupport, Integer> associationTypeMap = new HashMap<DataSupport, Integer>();

	/**
	 * A map to store all the associated models of self model as key, and does
	 * this associated model has foreign key column as value.
	 */
	private Map<DataSupport, Boolean> hasForeignKeyMap = new HashMap<DataSupport, Boolean>();

	/**
	 * Saves the model. <br />
	 * <br />
	 * If the model is new a record gets created in the database, otherwise the
	 * existing record gets updated. <br />
	 * Saving this model will also save all the models which have associations
	 * with it. If any of them fail to save, the whole action will be cancelled
	 * and your database will be <b>rolled back</b>. <br />
	 * If the model has a field named id or _id and field type is int or long,
	 * the id value generated by database will assign to it after the model is
	 * saved.
	 * 
	 * @return If the model and associated models are saved successfully, return
	 *         true. Any of them failed return false.
	 */
	public synchronized boolean save() {
		SQLiteDatabase db = Connector.getDatabase();
		db.beginTransaction();
		try {
			SaveHandler saveSupport = new SaveHandler();
			saveSupport.onSave(this, db);
			db.setTransactionSuccessful();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} finally {
			db.endTransaction();
		}
	}

	/**
	 * Get the baseObjId of this model if it's useful for developers. It's for
	 * system use usually. Do not try to assign or modify it.
	 * 
	 * @return The base object id.
	 */
	protected long getBaseObjId() {
		return baseObjId;
	}

	protected boolean isSaved() {
		return baseObjId > 0;
	}

	void addNewAssociatedModel(DataSupport associatedModel, int associationType,
			boolean hasForeignKeyColumn) {
		if (!associatedModels.contains(associatedModel)) {
			associatedModels.add(associatedModel);
			associationTypeMap.put(associatedModel, associationType);
			hasForeignKeyMap.put(associatedModel, hasForeignKeyColumn);
		}
	}

	/**
	 * Get the associated models of self model.
	 * 
	 * @return The associated models of self model.
	 */
	List<DataSupport> getAssociatedModels() {
		return associatedModels;
	}

	/**
	 * Judge a associated model has foreign key column or not.
	 * 
	 * @param associatedModel
	 *            The associated model use to judge.
	 * @return If the associated model has foreign key column, return true.
	 *         Otherwise return false.
	 */
	boolean hasForeignKeyColumn(DataSupport associatedModel) {
		if (hasForeignKeyMap.containsKey(associatedModel)) {
			return hasForeignKeyMap.get(associatedModel);
		}
		return false;
	}

	int getAssociationType(DataSupport associatedModel) {
		if (associationTypeMap.containsKey(associatedModel)) {
			return associationTypeMap.get(associatedModel);
		}
		return 0;
	}

}
